package com.ruoyi.common.utils;
import com.ruoyi.common.config.MinioConfig;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.messages.Item;
import io.minio.messages.Part;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.ruoyi.common.utils.SecurityUtils.getLoginUser;

@Component
public class MinioUtil {
    private static final Logger log = LoggerFactory.getLogger(MinioUtil.class);

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig minioConfig;

    @Value("${minio.avatarBucketName}")
    private String avatarBucketName;

    @Value("${minio.videoBucketName}")
    private String videoBucketName;

    @Value("${minio.coverBucketName}")
    private String coverBucketName;

    /**
     * 通用上传方法
     * @param file 文件
     * @param bucketName 存储桶
     * @param objectPrefix 对象前缀（支持占位符如%s,%d）
     * @param cleanOld 是否需要清理旧文件
     * @param args 前缀参数（用于格式化objectPrefix）
     * @return 完整URL
     */
    private String commonUpload(
            MultipartFile file,
            String bucketName,
            String objectPrefix,
            boolean cleanOld,
            Object... args) throws Exception {

        // 格式化对象前缀
        String formattedPrefix = String.format(objectPrefix, args);

        // 生成带UUID的文件名
        String objectName = formattedPrefix + UUID.randomUUID() + "_" + file.getOriginalFilename();

        try (InputStream inputStream = file.getInputStream()) {
            // 确保存储桶存在
            ensureBucketExists(bucketName);

            // 清理旧文件
            if (cleanOld) {
                cleanObjects(bucketName, formattedPrefix);
            }

            // 上传文件
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());

            return String.format("%s/%s/%s",
                    minioConfig.getEndpoint(),
                    bucketName,
                    objectName);
        } catch (Exception e) {
            log.error("[{}]上传失败", bucketName, e);
            throw new Exception(bucketName + "上传失败: " + e.getMessage());
        }
    }

    /**
     * 确保存储桶存在
     */
    private void ensureBucketExists(String bucketName) throws Exception {
        boolean found = minioClient.bucketExists(
                BucketExistsArgs.builder().bucket(bucketName).build());
        if (!found) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 清理指定前缀的所有对象
     */
    public void cleanObjects(String bucketName, String prefix) throws Exception {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(true)
                        .build());

        for (Result<Item> result : results) {
            Item item = result.get();
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(item.objectName())
                            .build());
            log.info("已删除旧文件：{}", item.objectName());
        }
    }

    // 上传头像
    public String uploadUserAvatar(MultipartFile file, Long userId) throws Exception {
        String prefix = String.format("user-%d/", userId);
        String url = commonUpload(file, avatarBucketName, prefix, true, userId);

        // 头像需要额外处理旧URL
        LoginUser loginUser = getLoginUser();
        SysUser user = loginUser.getUser();
        if (StringUtils.isNotEmpty(user.getAvatar())) {
            handleOldObject(user.getAvatar(), avatarBucketName);
        }
        return url;
    }

    // 上传培训视频
    public String uploadTrainingVideo(MultipartFile file, Long trainingId, Long videoId) throws Exception {
        String prefix = String.format("training-%d/video-%d/", trainingId, videoId);
        return commonUpload(file, videoBucketName, prefix, true, trainingId, videoId);
    }

    // 上传培训封面
    public String uploadTrainingImage(MultipartFile file, Long trainingId) throws Exception {
        String prefix = String.format("training-%d/", trainingId);
        return commonUpload(file, coverBucketName, prefix, true, trainingId);
    }

    /**
     * 处理旧对象（用于特殊清理场景）
     */
    private void handleOldObject(String oldUrl, String bucketName) {
        try {
            String prefixToRemove = minioConfig.getEndpoint() + "/" + bucketName + "/";
            String objectName = oldUrl.startsWith(prefixToRemove)
                    ? oldUrl.substring(prefixToRemove.length())
                    : oldUrl;

            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
        } catch (Exception e) {
            log.warn("旧文件清理失败: {}", oldUrl, e);
        }
    }

    // 获取视频存储桶名称
    public String getVideoBucketName() {
        return videoBucketName;
    }

    /**
     * 上传分片文件
     */
    public void uploadChunk(MultipartFile file, String bucketName, String objectName)
            throws Exception {
        try (InputStream inputStream = file.getInputStream()) {  // 自动资源管理
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, file.getSize(), -1)
                            .build()
            );
        } catch (Exception e) {
            log.error("[{}]分片上传失败，对象: {}", bucketName, objectName, e);  // 增强日志信息
            throw new Exception(bucketName + "分片上传失败: " + e.getMessage());
        }
    }

    /**
     * 合并分片文件
     */
    public String mergeChunks(String bucketName, String chunkPrefix, String targetObject)
            throws Exception {

        // 获取所有分片并排序
        List<String> chunkPaths = listChunkObjects(bucketName, chunkPrefix);
        chunkPaths.sort(Comparator.comparingInt(
                path -> Integer.parseInt(path.substring(path.lastIndexOf("/") + 1))));

        // 构建合并源
        List<ComposeSource> sources = chunkPaths.stream()
                .map(path -> ComposeSource.builder()
                        .bucket(bucketName)
                        .object(path)
                        .build())
                .collect(Collectors.toList());

        // 执行合并
        minioClient.composeObject(
                ComposeObjectArgs.builder()
                        .bucket(bucketName)
                        .object(targetObject)
                        .sources(sources)
                        .build());

        // 异步清理分片
        CompletableFuture.runAsync(() -> cleanChunks(bucketName, chunkPrefix));

        return getObjectUrl(bucketName, targetObject);
    }

    /**
     * 列出分片对象
     */
    public List<String> listChunkObjects(String bucketName, String prefix) {
        List<String> chunks = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(true)
                        .build());

        for (Result<Item> result : results) {
            try {
                chunks.add(result.get().objectName());
            } catch (Exception e) {
                log.error("获取分片列表失败", e);
            }
        }
        return chunks;
    }

    /**
     * 清理分片目录
     */
    public void cleanChunks(String bucketName, String prefix) {
        listChunkObjects(bucketName, prefix).forEach(path -> {
            try {
                minioClient.removeObject(
                        RemoveObjectArgs.builder()
                                .bucket(bucketName)
                                .object(path)
                                .build());
            } catch (Exception e) {
                log.error("分片清理失败: {}", path, e);
            }
        });
    }

    /**
     * 获取对象访问URL
     */
    public String getObjectUrl(String bucketName, String objectName) {
        return String.format("%s/%s/%s",
                minioConfig.getEndpoint(),
                bucketName,
                objectName);
    }

}